#pragma once

namespace refine {

/// The function is intended for processing of selected pixels, which
/// requires data from all pixels in surrounding window.
/// The machine can accumulate data of the window and allow
/// quick move to neighbour pixels without reaccumulation data in 
/// the intersection of old and new pixels' windows.
/// The machine should provide the following functions:
/// size_extent dim();
/// bool selected(size_point);
/// void add(size_point); /// add pixel from the window of to be processed pixel
/// void remove(size_point); /// remove pixel from the window of already processed pixel
/// void remove_all();
/// void process(size_point, size_rect window); /// process pixel after all the data from its window was accumulated
template <typename Machine>
void window_pass(Machine & m, int whs)
{
	size_extent dim = m.dim();
	int_rect frame(0, 0, dim.sx, dim.sy);
	int_rect accumulated_window(0, 0, 0, 0);

	size_point c; ///< center of the window
	for (c.y = 0; c.y < dim.sy; ++c.y) {
		//process even rows from left to right and odd rows in reverse direction
		int x0, xe, xd;
		if (c.y % 2 == 0) {
			x0 = 0;
			xe = dim.sx;
			xd = 1;
		}
		else {
			x0 = dim.sx - 1;
			xe = -1;
			xd = -1;
		}
		for (c.x = x0; c.x != xe; c.x += xd) {
			if (!m.selected(c))
				continue;
			int_rect window(c, c + size_point(1,1));
			window.inflate(whs, whs);
			intersect(frame, window, &window);
			size_point w;
			int_rect wisec;
			if (!intersect(window, accumulated_window, &wisec) || wisec.area() < (whs + 1) * (2 * whs + 1)) {
				//old and new windows are either not intersecting of the area of intersection is to small
				m.remove_all();
				for (w.y = window.y1; w.y < window.y2; ++w.y)
					for (w.x = window.x1; w.x < window.x2; ++w.x)
						m.add(w);
			}
			else {
				//remove all pixels of accumulated_window, which do not present in new window
				for (w.y = accumulated_window.y1; w.y < accumulated_window.y2; ++w.y)
					for (w.x = accumulated_window.x1; w.x < accumulated_window.x2; ++w.x)
						if (!window.inside(w))
							m.remove(w);
				//add all pixles of new window, which do not present in accumulated_window
				for (w.y = window.y1; w.y < window.y2; ++w.y)
					for (w.x = window.x1; w.x < window.x2; ++w.x)
						if (!accumulated_window.inside(w))
							m.add(w);
			}
			accumulated_window = window;
			m.process(c, accumulated_window);
		}
	}
}

} //namespace refine
